package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.AlertNotifyDto;
import com.zzyl.dto.AlertRuleDto;
import com.zzyl.entity.AlertData;
import com.zzyl.entity.AlertRule;
import com.zzyl.mapper.AlertRuleMapper;
import com.zzyl.service.AlertRuleService;
import com.zzyl.vo.AlertRuleVo;
import com.zzyl.vo.DeviceDataVo;
import com.zzyl.websocket.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * wangcong
 * 2025/2/16 10:36
 * @author 17284
 */
@Service
public class AlertRuleServiceImpl implements AlertRuleService {
    @Autowired
    private AlertRuleMapper alertRuleMapper;
     @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private WebSocketServer webSocketServer;
    @Override
    public void create(AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRuleMapper.insert(alertRule);

    }

    @Override
    public PageResponse<AlertRuleVo> getPage(Integer pageNum, Integer pageSize, String alertRuleName, String functionName, String productKey) {
        PageHelper.startPage(pageNum, pageSize);
        Page<AlertRuleVo> page = alertRuleMapper.pageQuery(alertRuleName, functionName, productKey);
        page.getResult().forEach(alertRuleVo -> {
            String rules = CharSequenceUtil.format("{}{}{},持续{}个周期就报警",
                    alertRuleVo.getFunctionName(),
                    alertRuleVo.getOperator(),
                    alertRuleVo.getValue(),
                    alertRuleVo.getDuration()
            );
            alertRuleVo.setRules(rules);
        });

        return PageResponse.of(page,AlertRuleVo.class);
    }

    @Override
    public AlertRuleVo read(Long id) {
        return alertRuleMapper.selectByPrimaryKey(id);
    }

    @Override
    public void update(Long id, AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRule.setId(id);
        alertRuleMapper.updateByPrimaryKey(alertRule);
    }
    


    @Override
    public void delete(Long id) {
        alertRuleMapper.deleteByPrimaryKey(id);
    }

    @Override
    public void updateStatus(Long id, Integer status) {
        alertRuleMapper.updateStatus(id, status);
    }

    @Override
    public List<AlertRuleVo> queryAllRules(String productKey, String functionId, String iotId) {
        return alertRuleMapper.queryAllRules(productKey, functionId, iotId);
    }

    @Override
    public void matchRule(DeviceDataVo deviceDataVo) {
        // 1. 判断上报时间是否是有效数据
        LocalDateTime alarmTime = deviceDataVo.getAlarmTime();
        long between = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.CENTURIES);
        if (between > 60){
            // 超时
            return;
        }
        // 2 . 获取所有规则, 包含产品规则以及设备单独的规则
        List<AlertRuleVo> small = alertRuleMapper.queryAllRules(deviceDataVo.getProductKey(), deviceDataVo.getFunctionId(), deviceDataVo.getIotId());
        List<AlertRuleVo> all = alertRuleMapper.queryAllRules(deviceDataVo.getProductKey(), deviceDataVo.getFunctionId(), "-1");
        // 2.1 整合
        Collection<AlertRuleVo> allRules = CollUtil.addAll(small, all);
        if (ObjectUtil.isEmpty(allRules)){
            return;
        }
        // 2.2 遍历规则  , 判断
        allRules.forEach(alertRuleVo -> {
            checkRule(deviceDataVo, alertRuleVo);
        });

        }
    @Value("${zzyl.alert.deviceMaintainerRole}")
    private String deviceMaintainerRole;
    @Value("${zzyl.alert.managerRole}")
    private String managerRole;

    private void checkRule(DeviceDataVo deviceDataVo, AlertRuleVo alertRuleVo) {
        String key = deviceDataVo.getIotId() + deviceDataVo.getProductKey() + deviceDataVo.getFunctionId();
        //1. 判断是否在有效期内
        String[] split = alertRuleVo.getAlertEffectivePeriod().split("~");
        LocalTime startTime = LocalTime.parse(split[0]);
        LocalTime endTime = LocalTime.parse(split[1]);
        LocalTime alarmTime = deviceDataVo.getAlarmTime().toLocalTime();
        if (alarmTime.isBefore(startTime) || alarmTime.isAfter(endTime)){
            // 不在有效期内
            return;
        }

        // 2. 校验数据阈值
        String dataValue = deviceDataVo.getDataValue();
        Float value = alertRuleVo.getValue();
        String operator = alertRuleVo.getOperator();
        int comResult = NumberUtil.compare(Float.parseFloat(dataValue), value);
        if ((comResult >= 0 && ">=".equals(operator)) || (comResult < 0 && "<".equals(operator))){
            // 此时的数据满足报警条件
            System.out.println("hello");
        }else{
            // 删除redis中的报警次数记录
            redisTemplate.delete(CacheConstants.DEVICE_ALARM_COUNT + key);
            return;
        }
        //3.判断沉默周期
        String s = redisTemplate.opsForValue().get(CacheConstants.DEVICE_SILENT_TIME + key);
        if (ObjectUtil.isNotEmpty(s)){
            // 有数据,说明还在沉默周期中
            return;
        }
        //4.判断报警次数
        String num = redisTemplate.opsForValue().get(CacheConstants.DEVICE_ALARM_COUNT + key);
        int count = ObjectUtil.isEmpty(num)? 1 : Integer.parseInt(num) + 1;
        if (count != alertRuleVo.getDuration()){
            // 未达到报警次数, 记录次数
            redisTemplate.opsForValue().set(CacheConstants.DEVICE_ALARM_COUNT + key, count+"");
            return;
        }
        //5.达到报警次数, 触发报警
        // 5.1 删除redis报警数
        redisTemplate.delete(CacheConstants.DEVICE_ALARM_COUNT + key);
        // 5.2 添加redis沉默周期
        Integer alertSilentPeriod = alertRuleVo.getAlertSilentPeriod();
        redisTemplate.opsForValue().set(CacheConstants.DEVICE_SILENT_TIME + key,"1",alertSilentPeriod, TimeUnit.MINUTES);
        List<Long> ids = new ArrayList<>();
        // 6.是否是老人数据
        if (alertRuleVo.getAlertDataType() == 0){
            // 老人数据  -- 找护理员
            if (deviceDataVo.getLocationType() == 0){
                // 随身设备
                ids = alertRuleMapper.selectNurseByElderId(deviceDataVo.getIotId());
            }else if (deviceDataVo.getLocationType() == 1 && deviceDataVo.getPhysicalLocationType() ==2){
                // 固定床上的设备
                ids = alertRuleMapper.selectNursingIdsByIotIdWithBed(deviceDataVo.getIotId());
            }
        }else {
            // 找到维修工
            ids =alertRuleMapper.selectCompairUserIds(deviceMaintainerRole);
        }

        // 超级管理员
        List<Long> managerIds = alertRuleMapper.selectManagerIds(managerRole);

        ids.addAll(managerIds);

        // 插入报警数据
        String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", alertRuleVo.getFunctionName(), alertRuleVo.getOperator(), alertRuleVo.getValue(), alertRuleVo.getDuration());
        AlertData alertData = BeanUtil.toBean(deviceDataVo, AlertData.class);
        alertData.setAlertRuleId(alertRuleVo.getId());
        alertData.setAlertReason(alertReason);
        alertData.setType(alertRuleVo.getAlertDataType());
        alertData.setStatus(0);

        ArrayList<AlertData> list = new ArrayList<>();
        ids.forEach(id -> {
            AlertData bean = BeanUtil.toBean(alertData, AlertData.class);
            bean.setUserId(id);
            list.add(bean);
        });

        // 批量插入
        alertRuleMapper.insertBatchAlertData(list);
        /// websocket 推送消息
        AlertNotifyDto alertNotifyDto = new AlertNotifyDto();
        AlertData d = list.get(0);
        BeanUtil.copyProperties(d, alertNotifyDto);
        alertNotifyDto.setFunctionName(alertRuleVo.getFunctionName());
        alertNotifyDto.setAlertDataType(alertData.getType());
        alertNotifyDto.setNotifyType(1);
        webSocketServer.sendMessageToConsumer(alertNotifyDto, ids);
    }
}
